package com.hiq.gene.engine;

import com.hiq.gene.exception.InvalidTypeException;
import com.hiq.gene.model.Entity;
import com.hiq.gene.model.Types;
import com.hiq.gene.util.StringUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author Yanis_Triandaphilov
 */
public class JpaGenerator {

    private final String basePackage;
    private final String srcDir;
    private String entityDir;

    public JpaGenerator(String srcDir, String basePackage) {
        this.srcDir = srcDir;
        this.basePackage = basePackage;
    }

    public void generate(List<Entity> entities) throws InvalidTypeException {
        createPackageDir();
        for (Entity e: entities) {
            generate(e);
        }
    }

    private void generate(Entity e) throws InvalidTypeException {
        List<String> imports = getImports(e);
        String fileName = entityDir + e.getName() + ".java";
        try {
            PrintWriter pw = new PrintWriter(fileName);
            pw.println("package " + basePackage + ".entity;");
            pw.println();
            printImports(pw, e);
            pw.println();
            pw.println("@Entity");
            pw.println("@Table(name = \"" + StringUtil.getDBName(e.getName()) + "\")");
            String seqName = e.getName().toLowerCase() + "Seq";
            String dbSeqName = StringUtil.getDBName(e.getName()).toLowerCase() + "_id_seq";
            pw.println("@SequenceGenerator(name = \"" + seqName + "\", sequenceName = \"" + dbSeqName + "\", allocationSize = 1)");
            pw.println("public class " + e.getName() + " implements IEntity {");
            printFields(pw, e);
            pw.println();
            printGettersAndSetters(pw, e);
            pw.println("}");
            pw.flush();
            pw.close();
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
    }

    private void printFields(PrintWriter pw, Entity e) throws InvalidTypeException {
        pw.println("    private Long id;");
        Set<String> names = e.getFields().keySet();
        for (String name: names) {
            String type = e.getFields().get(name);
            pw.print("    private ");
            String javaType = Types.getJavaType(type);
            pw.println(javaType + " " + name + ";");
        }
    }


    private void printGettersAndSetters(PrintWriter pw, Entity e) throws InvalidTypeException {
        printGetterAndSetterForId(pw, e);
        Set<String> names = e.getFields().keySet();
        for (String name: names) {
            String javaType = Types.getJavaType(e.getFields().get(name));
            printGetter(pw, name, javaType);
            printSetter(pw, name, javaType);
        }
    }

    private void printGetterAndSetterForId(PrintWriter pw, Entity e) {
        pw.println("    @Id");
        pw.println("    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = \"" + e.getName().toLowerCase() + "Seq" + "\")");
        pw.println("    @Column(name = \"ID\")");
        pw.println("    public Long getId() {");
        pw.println("        return id;");
        pw.println("    }");
        printSetter(pw, "id", "Long");
    }

    private void printSetter(PrintWriter pw, String name, String javaType) {
        pw.println("    public void " + StringUtil.getSetterName(name) + "(" + javaType + " " + name + ") {");
        pw.println("        this." + name + " = " + name + ";");
        pw.println("    }");
        pw.println();
    }


    private void printGetter(PrintWriter pw, String name, String javaType) {
        if (javaType.startsWith("@")) {
            pw.println("    @ManyToOne");
            pw.println("    @JoinColumn(name=\"" + name.substring(1).toUpperCase() + "_ID\")");
        } else {
            pw.println("    @Column(name = \"" + StringUtil.getDBName(name) + "\")");
        }

        pw.println("    public " + javaType + " " + StringUtil.getGetterName(name, false) + "() {");
        pw.println("        return this." + name + ";");
        pw.println("    }");
        pw.println();
    }


    private void printImports(PrintWriter pw, Entity e) {
        pw.println("import javax.persistence.*;");
        pw.println("import com.hiq.persistence.*;");
        for (String type : e.getFields().values()) {
            if ("datetime".equals(type)) {
                pw.println("import java.util.Date;");
                break;
            }
        }
    }

    private List<String> getImports(Entity e) {
        List<String> res = new ArrayList<String>();
        return res;
    }

    private void createPackageDir() {
        String[] dirs = basePackage.split("\\.");
        entityDir = srcDir + basePackage.replace(".", "/") + "/entity/";
        File f = new File(entityDir);
        boolean res = f.mkdir();
    }
}
